/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\miphelper.h"

//---------------------------------------------------------------------------

#define MAXIMUM_IP_ADAPTER_INFOS 25

//---------------------------------------------------------------------------

MIpHelper::MIpHelper(void)
{
	Initialize();
}

MIpHelper::~MIpHelper(void)
{
}

//---------------------------------------------------------------------------

void MIpHelper::Initialize(void)
{
	xAdapterName.Empty();
	xDescription.Empty();
	xHwAddrLen = 0;
	memset(xHwAddr, 0, MAX_ADAPTER_ADDRESS_LENGTH);
	xType = MIB_IF_TYPE_OTHER;
	xDhcpEnabled = false;
	xHaveWins = false;
	xIpAddr.Empty();
	xIpMask.Empty();
	xIpAddrGw.Empty();
	xIpMaskGw.Empty();
	xIpAddrDhcp.Empty();
	xIpMaskDhcp.Empty();
	xIpAddrWins1.Empty();
	xIpMaskWins1.Empty();
	xIpAddrWins2.Empty();
	xIpMaskWins2.Empty();
	xLeaseObtained = 0;
	xLeaseExpires  = 0;
	xAdapterIndex = 0xFFFFFFFF;

	memset(&xIPv6Unicast,   0, sizeof(sockaddr_in6));
	memset(&xIPv6Anycast,   0, sizeof(sockaddr_in6));
	memset(&xIPv6Multicast, 0, sizeof(sockaddr_in6));
	memset(&xIPv6DNS,       0, sizeof(sockaddr_in6));

	memset(macAddress, 0, 6);
	ipAddress.S_un.S_addr = 0;
	ipMask.S_un.S_addr    = 0;
	ipAddrGw.S_un.S_addr  = 0;

	xHostName.Empty();
	xDomainName.Empty();
	xDnsServer.Empty();

	memset(macDnsServer, 0, 6);
	ipAddrDns.S_un.S_addr = 0;
}

void MIpHelper::LoadFromAdapterInfo(IP_ADAPTER_INFO* adapterIn)
{
	int i;

	Initialize();
	if(adapterIn==NULL) return;

	xAdapterName   = adapterIn->AdapterName;
	xDescription   = adapterIn->Description;
	xHwAddrLen     = adapterIn->AddressLength;
	memcpy(xHwAddr, adapterIn->Address, MAX_ADAPTER_ADDRESS_LENGTH);
	xType          = adapterIn->Type;
	xDhcpEnabled   = adapterIn->DhcpEnabled ? true : false;
	xHaveWins      = adapterIn->HaveWins ? true : false;
	xIpAddr        = (char*)adapterIn->IpAddressList.IpAddress.String;
	xIpMask        = (char*)adapterIn->IpAddressList.IpMask.String;
	xIpAddrGw      = (char*)adapterIn->GatewayList.IpAddress.String;
	xIpMaskGw      = (char*)adapterIn->GatewayList.IpMask.String;
	xIpAddrDhcp    = (char*)adapterIn->DhcpServer.IpAddress.String;
	xIpMaskDhcp    = (char*)adapterIn->DhcpServer.IpMask.String;
	xIpAddrWins1   = (char*)adapterIn->PrimaryWinsServer.IpAddress.String;
	xIpMaskWins1   = (char*)adapterIn->PrimaryWinsServer.IpMask.String;
	xIpAddrWins2   = (char*)adapterIn->SecondaryWinsServer.IpAddress.String;
	xIpMaskWins2   = (char*)adapterIn->SecondaryWinsServer.IpMask.String;
	xLeaseObtained = adapterIn->LeaseObtained;
	xLeaseExpires  = adapterIn->LeaseExpires;
	xAdapterIndex  = adapterIn->Index;

	for(i=0; i<6 && i<MAX_ADAPTER_ADDRESS_LENGTH; i++)
		macAddress[i] = xHwAddr[i];

	ipAddress.S_un.S_addr = inet_addr(xIpAddr);
	ipMask.S_un.S_addr    = inet_addr(xIpMask);
	ipAddrGw.S_un.S_addr  = inet_addr(xIpAddrGw);
}

//---------------------------------------------------------------------------

bool MIpHelper::GetNetworkParameters(void)
{
	BYTE* buff;
	ULONG size;
	DWORD res;
	FIXED_INFO* fi;

	buff = NULL;
	size = 0;
	res = GetNetworkParams((FIXED_INFO*)buff, &size);
	if(res!=ERROR_BUFFER_OVERFLOW)
		return false;
	
	try{
		buff = new BYTE[size];
	}
	catch(...){
		buff = NULL;
	}
	if(buff==NULL) return false;

	res = GetNetworkParams((FIXED_INFO*)buff, &size);
	if(res!=ERROR_SUCCESS){
		delete[] buff;
		return false;
	}

	fi = (FIXED_INFO*)buff;
	xHostName   = fi->HostName;
	xDomainName = fi->DomainName;
	xDnsServer  = fi->DnsServerList.IpAddress.String;

	ipAddrDns.S_un.S_addr = inet_addr(xDnsServer);

	delete[] buff;
	return true;
}

//---------------------------------------------------------------------------

bool MIpHelper::FindAdapter(CString strIn)
{
	IP_ADAPTER_INFO *adapterInfo;
	IP_ADAPTER_INFO *adapter;
	int adapterInfoCnt;
	bool found;
	DWORD size;
	DWORD res;
	CString str;

	Initialize();

	found = false;

	adapterInfoCnt = MAXIMUM_IP_ADAPTER_INFOS;

	try{
		adapterInfo = new IP_ADAPTER_INFO[adapterInfoCnt];
	}
	catch(...){
		adapterInfo = NULL;
	}

	if(adapterInfo==NULL) return false;

	size = adapterInfoCnt * sizeof(IP_ADAPTER_INFO);
	res = GetAdaptersInfo(&adapterInfo[0], &size);
	if(res==ERROR_SUCCESS || res==ERROR_BUFFER_OVERFLOW){
		str = strIn.Mid(20);
		adapter = &adapterInfo[0];
		while(adapter!=NULL && !found){
			if(str.CompareNoCase(adapter->AdapterName)==0){
				found = true;
				LoadFromAdapterInfo(adapter);
			}
			else
				adapter = adapter->Next;
		}
	}

	delete[] adapterInfo;

	found &= GetNetworkParameters();

	if(found)
		GetIPv6Address();

	return found;
}

//---------------------------------------------------------------------------

void MIpHelper::GetIPv6Address(void)
{
	IP_ADAPTER_ADDRESSES *addrInfos;
	IP_ADAPTER_ADDRESSES *someAddr;
	SOCKET_ADDRESS       sockAddr;
	sockaddr_in6         *sockAddr6;
	int     adapterInfoCnt;
	bool    found;
	CString str;
	DWORD   size;
	DWORD   res;

	adapterInfoCnt = MAXIMUM_IP_ADAPTER_INFOS;

	try{
		addrInfos = new IP_ADAPTER_ADDRESSES[adapterInfoCnt];
	}
	catch(...){
		addrInfos = NULL;
	}
	if(addrInfos==NULL) return;

	size = adapterInfoCnt * sizeof(IP_ADAPTER_ADDRESSES);
	res = GetAdaptersAddresses(AF_INET6, 0, NULL, addrInfos, &size);
	if(res!=ERROR_SUCCESS){
		delete[] addrInfos;
		return;
	}

	found = false;
	someAddr = &addrInfos[0];
	while(someAddr!=NULL && !found){
		str = someAddr->AdapterName;
		if(str==xAdapterName){
			if(someAddr->FirstUnicastAddress!=NULL){
				sockAddr = someAddr->FirstUnicastAddress->Address;
				if(sockAddr.iSockaddrLength==sizeof(SOCKADDR_IN6)){
					sockAddr6 = (sockaddr_in6*)sockAddr.lpSockaddr;
					if(sockAddr6!=NULL){
						memcpy(&xIPv6Unicast, sockAddr6, sizeof(sockaddr_in6));
						found = true;
					}
				}
			}

			if(found){
				if(someAddr->FirstAnycastAddress!=NULL){
					sockAddr = someAddr->FirstAnycastAddress->Address;
					if(sockAddr.iSockaddrLength==sizeof(SOCKADDR_IN6)){
						sockAddr6 = (sockaddr_in6*)sockAddr.lpSockaddr;
						if(sockAddr6!=NULL)
							memcpy(&xIPv6Anycast, sockAddr6, sizeof(sockaddr_in6));
					}
				}
				if(someAddr->FirstMulticastAddress!=NULL){
					sockAddr = someAddr->FirstMulticastAddress->Address;
					if(sockAddr.iSockaddrLength==sizeof(SOCKADDR_IN6)){
						sockAddr6 = (sockaddr_in6*)sockAddr.lpSockaddr;
						if(sockAddr6!=NULL)
							memcpy(&xIPv6Multicast, sockAddr6, sizeof(sockaddr_in6));
					}
				}
				if(someAddr->FirstMulticastAddress!=NULL){
					sockAddr = someAddr->FirstMulticastAddress->Address;
					if(sockAddr.iSockaddrLength==sizeof(SOCKADDR_IN6)){
						sockAddr6 = (sockaddr_in6*)sockAddr.lpSockaddr;
						if(sockAddr6!=NULL)
							memcpy(&xIPv6DNS, sockAddr6, sizeof(sockaddr_in6));
					}
				}
			}
		}
		
		someAddr = someAddr->Next;
	}

	delete[] addrInfos;
}

//---------------------------------------------------------------------------

bool MIpHelper::IpAddrBelongsToLocalNet(DWORD addrIn)
{
	DWORD localNet;
	DWORD ownerNet;

	localNet = ipAddress.S_un.S_addr & ipMask.S_un.S_addr;
	ownerNet = addrIn                & ipMask.S_un.S_addr;

	return localNet==ownerNet ? true : false;
}

//---------------------------------------------------------------------------

void MIpHelper::GetIPv6UnicastAddress(in_addr6* addrIn)
{
	if(addrIn==NULL) return;
	memcpy(addrIn->u.Byte, xIPv6Unicast.sin6_addr.u.Byte, 16);
}

DWORD MIpHelper::GetIPv6UnicastAddressScope(void)
{
	return xIPv6Unicast.sin6_scope_id;
}

void MIpHelper::GetIPv6AnycastAddress(in_addr6* addrIn)
{
	if(addrIn==NULL) return;
	memcpy(addrIn->u.Byte, xIPv6Anycast.sin6_addr.u.Byte, 16);
}

void MIpHelper::GetIPv6MulticastAddress(in_addr6* addrIn)
{
	if(addrIn==NULL) return;
	memcpy(addrIn->u.Byte, xIPv6Multicast.sin6_addr.u.Byte, 16);
}

void MIpHelper::GetIPv6DNSAddress(in_addr6* addrIn)
{
	if(addrIn==NULL) return;
	memcpy(addrIn->u.Byte, xIPv6DNS.sin6_addr.u.Byte, 16);
}

//---------------------------------------------------------------------------

bool MIpHelper::FlushArpTable(void)
{
	DWORD res;

	if(xAdapterIndex==0xFFFFFFFF) return false;

	res = FlushIpNetTable(xAdapterIndex);

	return res==NO_ERROR ? true : false;
}

bool MIpHelper::AddArpTableEntry(DWORD ipIn, BYTE* macIn)
{
	MIB_IPNETROW entry;
	DWORD res;
	int i;

	if(macIn==NULL) return false;
	if(xAdapterIndex==0xFFFFFFFF) return false;

	if(MAXLEN_PHYSADDR<6) return false;		// should not happend!, used in entry.bPhysAddr

	entry.dwIndex = xAdapterIndex;
	entry.dwPhysAddrLen = 6;
	for(i=0; i<6; i++)
		entry.bPhysAddr[i] = macIn[i];
	entry.dwAddr = ipIn;
	entry.dwType = 3;	// dynamic
	res = CreateIpNetEntry(&entry);

	return res==NO_ERROR ? true : false;
}

//---------------------------------------------------------------------------
